<!DOCTYPE html>
<html lang="en">

<!-- Head tag -->
<head><meta name="generator" content="Hexo 3.8.0">

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!--Description-->

    

    
        <meta name="description" content="一、效果及功能实现的功能：

基于FFMpeg，兼容Mac、window和Linux平台；
支持annexb(00 00 00 01)开头及4字节长度开头的h264数据解析播放(前两帧必须为sps和pps);
显示图像宽度，fps和当前帧nalu type，大小及在文件中的偏移位置；
支持上一帧/下">
    

    <!--Author-->
    
        <meta name="author" content="Cyrus">
    

    <!--Open Graph Title-->
    
        <meta property="og:title" content="QT H.264播放器">
    

    <!--Open Graph Description-->
    
        <meta property="og:description" content="一、效果及功能实现的功能：

基于FFMpeg，兼容Mac、window和Linux平台；
支持annexb(00 00 00 01)开头及4字节长度开头的h264数据解析播放(前两帧必须为sps和pps);
显示图像宽度，fps和当前帧nalu type，大小及在文件中的偏移位置；
支持上一帧/下">
    

    <!--Open Graph Site Name-->
        <meta property="og:site_name" content="Cyrus的技术空间">

    <!--Type page-->
    
        <meta property="og:type" content="article">
    

    <!--Page Cover-->
    
    
        <meta property="og:image" content="http://cyrus_huang.gitee.io/bloghttp://www.codeblocq.com/assets/projects/hexo-theme-clean-blog/img/home-bg.jpg">
    

        <meta name="twitter:card" content="summary_large_image">

    

    
        <meta name="twitter:image" content="http://cyrus_huang.gitee.io/bloghttp://www.codeblocq.com/assets/projects/hexo-theme-clean-blog/img/home-bg.jpg">
    

    <!-- Title -->
    
    <title>QT H.264播放器 - Cyrus的技术空间</title>

    <!-- Bootstrap Core CSS -->
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">

    <!-- Custom CSS -->
    <link rel="stylesheet" href="/blog/css/style.css">

    <!-- Custom Fonts -->
    <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" type="text/css">
    <link href="//fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic" rel="stylesheet" type="text/css">
    <link href="//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">

    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
    <script src="//oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->

    <!-- Gallery -->
    <link href="//cdnjs.cloudflare.com/ajax/libs/featherlight/1.3.5/featherlight.min.css" type="text/css" rel="stylesheet">

    <!-- Google Analytics -->
    


    <!-- favicon -->
    

</head>


<body>

    <!-- Menu -->
    <!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header page-scroll">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/blog/">Cyrus的技术空间</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav navbar-right">
                
                    <li>
                        <a href="/blog/">
                            
                                Home
                            
                        </a>
                    </li>
                
                    <li>
                        <a href="/blog/archives">
                            
                                Archives
                            
                        </a>
                    </li>
                
                    <li>
                        <a href="/blog/tags">
                            
                                Tags
                            
                        </a>
                    </li>
                
                    <li>
                        <a href="/blog/categories">
                            
                                Categories
                            
                        </a>
                    </li>
                
                    <li>
                        <a href="https://github.com/klugjo/hexo-theme-clean-blog">
                            
                                <i class="fa fa-github fa-stack-2x"></i>
                            
                        </a>
                    </li>
                
            </ul>
        </div>
        <!-- /.navbar-collapse -->
    </div>
    <!-- /.container -->
</nav>

    <!-- Main Content -->
    <!-- Page Header -->
<!-- Set your background image for this header in your post front-matter: cover -->

<header class="intro-header" style="background-image: url('http://www.codeblocq.com/assets/projects/hexo-theme-clean-blog/img/home-bg.jpg')">
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <div class="post-heading">
                    <h1>QT H.264播放器</h1>
                    
                    <span class="meta">
                        <!-- Date and Author -->
                        
                            Posted by Cyrus on
                        
                        
                            2019-09-29
                        
                    </span>
                </div>
            </div>
        </div>
    </div>
</header>

<!-- Post Content -->
<article>
    <div class="container">
        <div class="row">

            <!-- Tags and categories -->
           
                <div class="col-lg-4 col-lg-offset-2 col-md-5 col-md-offset-1 post-tags">
                    
                        


<a href="/blog/tags/h264/">#h264</a>


                    
                </div>
                <div class="col-lg-4 col-md-5 post-categories">
                    
                        

<a href="/blog/categories/音视频/">音视频</a>

                    
                </div>
            

            <!-- Gallery -->
            

            <!-- Post Main Content -->
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <h3 id="一、效果及功能"><a href="#一、效果及功能" class="headerlink" title="一、效果及功能"></a>一、效果及功能</h3><p><img src="http://cyrus_huang.gitee.io/blog/2019/09/29/QT-H-264播放器/result_1.png" alt=""><br>实现的功能：</p>
<ul>
<li>基于FFMpeg，兼容Mac、window和Linux平台；</li>
<li>支持annexb(00 00 00 01)开头及4字节长度开头的h264数据解析播放(前两帧必须为sps和pps);</li>
<li>显示图像宽度，fps和当前帧nalu type，大小及在文件中的偏移位置；</li>
<li>支持上一帧/下一帧单帧解析播放及自动播放。</li>
</ul>
<h3 id="二、具体实现"><a href="#二、具体实现" class="headerlink" title="二、具体实现"></a>二、具体实现</h3><h4 id="1、FFMpeg及跨平台配置"><a href="#1、FFMpeg及跨平台配置" class="headerlink" title="1、FFMpeg及跨平台配置"></a>1、FFMpeg及跨平台配置</h4><p>详见前面的FFMpeg播放器</p>
<h4 id="2、具体步骤"><a href="#2、具体步骤" class="headerlink" title="2、具体步骤"></a>2、具体步骤</h4><ul>
<li><p>1、遍历文件，判断是否h264模式文件，是否annexb格式，把文件中的nalu类型，偏移记录下来</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line">// FrameInfo 记录帧类型、大小和偏移</span><br><span class="line"></span><br><span class="line">class FrameInfo</span><br><span class="line">&#123;</span><br><span class="line">public:</span><br><span class="line">    FrameInfo();</span><br><span class="line"></span><br><span class="line">    int nalu_type;</span><br><span class="line">    int nalu_size;</span><br><span class="line">    int nalu_offset;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">#endif // FRAMEINFO_H</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// MainWindow.m</span><br><span class="line">void MainWindow::readFileThrought(QString path)</span><br><span class="line">&#123;</span><br><span class="line">    file = fopen(path.toUtf8().data(), &quot;rb&quot;);</span><br><span class="line">    if(file == NULL) &#123;</span><br><span class="line">        qDebug() &lt;&lt; &quot;fopen error!&quot; &lt;&lt; endl;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    uint8_t header[5];</span><br><span class="line">    if(fread(header, 1, 5, file) != 5) return;</span><br><span class="line"></span><br><span class="line">    if((header[0] == 0x00) &amp;&amp; (header[1] == 0x00) &amp;&amp; (header[2] == 0x00) &amp;&amp; (header[3] == 0x01) &amp;&amp; ((header[4] &amp; 0x1F) == 0x07)) &#123;</span><br><span class="line">        b_annexb = true;</span><br><span class="line">    &#125; else if ((header[0] == 0x00) &amp;&amp; (header[1] == 0x00) &amp;&amp; (header[2] == 0x01) &amp;&amp; ((header[3] &amp; 0x1F) == 0x07)) &#123;</span><br><span class="line">        b_annexb = true;</span><br><span class="line">    &#125; else if (((header[0] &lt;&lt; 24 + header[1] &lt;&lt; 16 + header[2] &lt;&lt; 8 + header[3]) &lt; 256) &amp;&amp; ((header[4] &amp; 0x1F) == 0x07)) &#123;</span><br><span class="line">        b_annexb = false;</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">        ui-&gt;infoLabel-&gt;setText(&quot;file format error!&quot;);</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    fseek(file, 0, SEEK_SET);</span><br><span class="line">    if(b_annexb) &#123;      // 00 00 00 01开头</span><br><span class="line">        int inputMaxSize = 1920 * 1080 * 3;</span><br><span class="line">        uint8_t *buffer = (uint8_t *)malloc(inputMaxSize);</span><br><span class="line">        fpos_t  current = 0;</span><br><span class="line">        int     readSize = 0;</span><br><span class="line"></span><br><span class="line">        while((readSize = fread(buffer, 1, inputMaxSize, file)) != 0) &#123;</span><br><span class="line">            int     pos = 0;</span><br><span class="line">            int     len = 0;</span><br><span class="line">            FrameInfo frame;</span><br><span class="line"></span><br><span class="line">            while ((len = readOneNaluFromBuffer(buffer, readSize, pos, frame))) &#123;</span><br><span class="line">                if (!frames.isEmpty()) &#123;</span><br><span class="line">                    FrameInfo aFrame = frames.last();</span><br><span class="line">                    frame.nalu_offset = aFrame.nalu_offset + aFrame.nalu_size + 4;</span><br><span class="line">                &#125; else &#123;</span><br><span class="line">                    frame.nalu_offset = 0;</span><br><span class="line">                &#125;</span><br><span class="line">                frames.append(frame);</span><br><span class="line">                pos += len;</span><br><span class="line">                current += len;</span><br><span class="line">            &#125;</span><br><span class="line">            fseek(file, current, SEEK_SET);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; else &#123;</span><br><span class="line"></span><br><span class="line">        uint32_t length = 0;</span><br><span class="line">        uint8_t  type = 0;</span><br><span class="line">        fseek(file,0L,SEEK_END);</span><br><span class="line">        int size=ftell(file);</span><br><span class="line">        fpos_t cur = 0;</span><br><span class="line"></span><br><span class="line">        while (cur &lt; size) &#123;</span><br><span class="line">            fseek(file, cur, SEEK_SET);</span><br><span class="line">            FrameInfo frame;</span><br><span class="line">            frame.nalu_offset = cur;</span><br><span class="line"></span><br><span class="line">            fread(&amp;length, 1, 4, file);</span><br><span class="line">            cur += 4;</span><br><span class="line">            fread(&amp;type, 1, 1, file);</span><br><span class="line">            frame.nalu_type = type &amp; 0x1F;</span><br><span class="line">            frame.nalu_size = ntohl(length);</span><br><span class="line">            cur += (frame.nalu_size);</span><br><span class="line">            frames.append(frame);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>2、下一帧实现</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">void MainWindow::onNextFrameButtonAction()</span><br><span class="line">&#123;</span><br><span class="line">    qDebug() &lt;&lt; &quot;MainWindow::onNextFrameButtonAction()&quot; &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    if(!frames.isEmpty() &amp;&amp; frames.size() &gt; ++m_frameIndex) &#123;</span><br><span class="line">        showFrame(m_frameIndex);</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">    	// 播到最后一帧，自动播放停止</span><br><span class="line">        if(pPlayTimer-&gt;isActive()) &#123;</span><br><span class="line">            pPlayTimer-&gt;stop();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 解码及更新帧信息</span><br><span class="line">void MainWindow::showFrame(int index)</span><br><span class="line">&#123;</span><br><span class="line">    FrameInfo frame = frames.at(index);</span><br><span class="line">    qDebug() &lt;&lt; &quot;frame type:&quot; &lt;&lt; frame.nalu_type &lt;&lt; &quot; frame offset:&quot; &lt;&lt; frame.nalu_offset &lt;&lt; &quot; frame size:&quot; &lt;&lt; frame.nalu_size &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    offset = frame.nalu_offset;</span><br><span class="line">    fseek(file, offset, SEEK_SET);</span><br><span class="line">    fread(pFrameData, 1, frame.nalu_size + 4, file);</span><br><span class="line">    showFrameInfo(frame);</span><br><span class="line">    if(!b_annexb) &#123;</span><br><span class="line">        const char header[] = &#123;0x00, 0x00, 0x00, 0x01&#125;;</span><br><span class="line">        memcpy(pFrameData, header, 4);</span><br><span class="line">    &#125;</span><br><span class="line">    pDecoder-&gt;decodeH264Frame(pFrameData, frame.nalu_size+4);</span><br><span class="line">    updateUI();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
</ul>
<p>// 解码<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br></pre></td><td class="code"><pre><span class="line">// 解码器初始化</span><br><span class="line">H264Decoder::H264Decoder()</span><br><span class="line">&#123;</span><br><span class="line">    decodeCallBack = NULL;</span><br><span class="line">    infoCallBack = NULL;</span><br><span class="line">    pCodec = NULL;</span><br><span class="line">    pCodecCtx = NULL;</span><br><span class="line">    pVideoFrame = NULL;</span><br><span class="line">    pSwsContext = NULL;</span><br><span class="line">    m_fps = 0;</span><br><span class="line">    m_timeBase = 0;</span><br><span class="line"></span><br><span class="line">    //注册FFmpeg所有编解码器</span><br><span class="line">    av_register_all();</span><br><span class="line">    //注册编解码器</span><br><span class="line">    //只有调用了该函数，才能使用编解码器</span><br><span class="line">    avcodec_register_all();</span><br><span class="line"></span><br><span class="line">    //根据类型创建解码器</span><br><span class="line">    //对应avcodec_find_encoder() 创建编码器</span><br><span class="line">    pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);</span><br><span class="line">    if (!pCodec) &#123;</span><br><span class="line">        printf(&quot;codec not find\n&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    //视音频流对应的结构体，用于视音频编解码</span><br><span class="line">    pCodecCtx = avcodec_alloc_context3(pCodec);</span><br><span class="line">    if (!pCodecCtx) &#123;</span><br><span class="line">        printf(&quot;allocate codec context error\n&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    //将解码器跟上下文联系起来</span><br><span class="line">    avcodec_open2(pCodecCtx, pCodec, NULL);</span><br><span class="line"></span><br><span class="line">    //创建avFrame avcodec_alloc_frame()已废弃</span><br><span class="line">    //存储非压缩的数据（视频对应RGB/YUV像素数据，音频对应PCM采样数据）--&gt;解码后的数据</span><br><span class="line">    pVideoFrame = av_frame_alloc();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 设置解码回调</span><br><span class="line">void H264Decoder::setDecodeCallBack(H264DecodeCallBack cb, void *userInfo)</span><br><span class="line">&#123;</span><br><span class="line">    decodeCallBack = cb;</span><br><span class="line">    this-&gt;userInfo = userInfo;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int H264Decoder::decodeH264Frame(unsigned char *inputBuffer, int aLength)</span><br><span class="line">&#123;</span><br><span class="line">    int gotPicPtr = 0;</span><br><span class="line">    int result = 0;</span><br><span class="line"></span><br><span class="line">    av_init_packet(&amp;pAvPackage);</span><br><span class="line">    pAvPackage.data = (unsigned char*)inputBuffer;</span><br><span class="line">    pAvPackage.size = aLength;</span><br><span class="line"></span><br><span class="line">    //解码</span><br><span class="line">    result = avcodec_decode_video2(pCodecCtx, pVideoFrame, &amp;gotPicPtr, &amp;pAvPackage);</span><br><span class="line"></span><br><span class="line">    if (gotPicPtr) &#123;</span><br><span class="line">        handleVideoFrame();</span><br><span class="line">        if(m_fps == 0 &amp;&amp; m_timeBase == 0) &#123;</span><br><span class="line">            avStreamFPSTimeBase(pCodecCtx, 0.04, &amp;this-&gt;m_fps, &amp;m_timeBase);</span><br><span class="line">            qDebug() &lt;&lt; &quot;timebase:&quot; &lt;&lt; m_timeBase &lt;&lt; &quot;  fps:&quot; &lt;&lt; m_fps &lt;&lt; endl;</span><br><span class="line">            if(infoCallBack != NULL) &#123;</span><br><span class="line">                QString info = QString(&quot;分辨率: %1*%2 fps: %3&quot;).arg(pCodecCtx-&gt;width).arg(pCodecCtx-&gt;height).arg(m_fps);</span><br><span class="line">                infoCallBack(userInfo, info);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    av_free_packet(&amp;pAvPackage);</span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">void H264Decoder::handleVideoFrame()</span><br><span class="line">&#123;</span><br><span class="line">    qDebug() &lt;&lt; &quot;MPMovieDecoder::handleVideoFrame()&quot; &lt;&lt; endl;</span><br><span class="line">    if (pVideoFrame-&gt;data[0] == NULL) &#123;</span><br><span class="line">        qDebug() &lt;&lt; &quot;_videoFrame-&gt;data[0] no data&quot; &lt;&lt; endl;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    if (pSwsContext == NULL) &#123;</span><br><span class="line">        bool result = setupScaler();</span><br><span class="line">        if (!result) &#123;</span><br><span class="line">            qDebug() &lt;&lt; &quot;fail setup video scaler&quot; &lt;&lt; endl;</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    qDebug() &lt;&lt; &quot;before sws_scale  width: &quot; &lt;&lt; pCodecCtx-&gt;width &lt;&lt; &quot;height  &quot; &lt;&lt; pCodecCtx-&gt;height &lt;&lt;  endl;</span><br><span class="line">    qDebug() &lt;&lt; &quot;_videoFrame.linesize: &quot; &lt;&lt; pVideoFrame-&gt;linesize[0] &lt;&lt; endl;</span><br><span class="line">    int height = sws_scale(pSwsContext,</span><br><span class="line">              (const uint8_t **)pVideoFrame-&gt;data,</span><br><span class="line">              pVideoFrame-&gt;linesize,</span><br><span class="line">              0,</span><br><span class="line">              pCodecCtx-&gt;height,</span><br><span class="line">              m_picture.data,</span><br><span class="line">              m_picture.linesize);</span><br><span class="line">    qDebug() &lt;&lt; &quot;sws_scale result &quot; &lt;&lt; height &lt;&lt; endl;</span><br><span class="line">	</span><br><span class="line">    // 回调</span><br><span class="line">    if(decodeCallBack != NULL) &#123;</span><br><span class="line">        decodeCallBack(userInfo, pCodecCtx-&gt;width, pCodecCtx-&gt;height, m_picture.data[0], m_picture.linesize[0] * pCodecCtx-&gt;height);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>// 主界面中回调显示图像<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">// MainWindow.m</span><br><span class="line"></span><br><span class="line">void MainWindow::H264DecodeCallBack(void *userInfo, int width, int height, uint8_t *frameData, int frameSize)</span><br><span class="line">&#123;</span><br><span class="line">    qDebug() &lt;&lt; &quot;void MainWindow::H264DecodeCallBack&quot; &lt;&lt; endl;</span><br><span class="line">    const int imageSize = (((width * 24 + 31) &gt;&gt; 5) &lt;&lt; 2) * height;</span><br><span class="line">    qDebug() &lt;&lt; &quot;imageSize &quot; &lt;&lt; imageSize &lt;&lt; &quot;  width * height * 3 =&quot; &lt;&lt; width * height * 3 &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    uint8_t *dataTmp = new uint8_t[imageSize];</span><br><span class="line">    //将数据拷贝到一个临时内存中，因为OnRecvFrame是异步执行的，data可能已经无效。</span><br><span class="line">    try</span><br><span class="line">    &#123;</span><br><span class="line">        memcpy(dataTmp, frameData, imageSize);</span><br><span class="line">    &#125;</span><br><span class="line">    catch (...)</span><br><span class="line">    &#123;</span><br><span class="line">        delete[] dataTmp;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    //转化rgb数据为可显示的图像对象。</span><br><span class="line">    QImage image = QImage(dataTmp, width, height, QImage::Format_RGB888);</span><br><span class="line">    if (image.isNull())</span><br><span class="line">    &#123;</span><br><span class="line">        qDebug()&lt;&lt;&quot;Receive frame error, width:%d, height:%d.&quot;&lt;&lt;width&lt;&lt;height;</span><br><span class="line"></span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    QImage m_image = image.copy(0, 0, width, height);</span><br><span class="line"></span><br><span class="line">    delete[] dataTmp;</span><br><span class="line"></span><br><span class="line">    MainWindow *w = (MainWindow *)userInfo;</span><br><span class="line">    w-&gt;ui-&gt;displayLabel-&gt;setPixmap(QPixmap::fromImage(m_image));</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<ul>
<li><p>3、上一帧<br>由于h264的B、P帧需要参考前面的I帧、P帧解码，所以，上一帧不是I帧的，我们先找到之前的I帧，并一路解码到上一帧显示</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">void MainWindow::onPreFrameButtonAction()</span><br><span class="line">&#123;</span><br><span class="line">    qDebug() &lt;&lt; &quot;MainWindow::onPreFrameButtonAction()&quot; &lt;&lt; endl;</span><br><span class="line">    if(--m_frameIndex &lt; 0) &#123;</span><br><span class="line">        m_frameIndex = 0;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    FrameInfo frame = frames.at(m_frameIndex);</span><br><span class="line">    // B/P帧</span><br><span class="line">    if (frame.nalu_type == 1) &#123;</span><br><span class="line">        for (int i = m_frameIndex-1; i &gt;= 0; i--) &#123;</span><br><span class="line">            FrameInfo frame = frames.at(i);</span><br><span class="line">            // 找到上一帧关键，一路解码到上一帧的前一帧</span><br><span class="line">            if(frame.nalu_type ==  5) &#123;</span><br><span class="line">                decodeFrames(i, m_frameIndex-1);</span><br><span class="line">                break;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    // 显示上一帧</span><br><span class="line">    showFrame(m_frameIndex);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 前一个关键帧到指定帧，速度快，基本无感</span><br><span class="line">void MainWindow::decodeFrames(int from, int to)</span><br><span class="line">&#123;</span><br><span class="line">    for (int i = from; i &lt;= to; i++) &#123;</span><br><span class="line">        showFrame(i);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>4、自动播放</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">void MainWindow::onPlayButtonAction()</span><br><span class="line">&#123;</span><br><span class="line">    if(file == NULL || frames.isEmpty()) return;</span><br><span class="line"></span><br><span class="line">    b_playing = !b_playing;</span><br><span class="line">    if(b_playing) &#123;</span><br><span class="line">        pPlayTimer-&gt;start(pDecoder-&gt;getTimeBase() * 1000);</span><br><span class="line">        ui-&gt;playButton-&gt;setText(&quot;stop&quot;);</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">        if(pPlayTimer-&gt;isActive()) &#123;</span><br><span class="line">            pPlayTimer-&gt;stop();</span><br><span class="line">        &#125;</span><br><span class="line">        ui-&gt;playButton-&gt;setText(&quot;play&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">void MainWindow::handlePlayTimeout()</span><br><span class="line">&#123;</span><br><span class="line">    if(pPlayTimer-&gt;isActive()) &#123;</span><br><span class="line">        pPlayTimer-&gt;stop();</span><br><span class="line">    &#125;</span><br><span class="line">    onNextFrameButtonAction();</span><br><span class="line">    if(b_playing) &#123;</span><br><span class="line">        pPlayTimer-&gt;start(pDecoder-&gt;getTimeBase() * 1000);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>


                
            </div>

            <!-- Comments -->
            
                <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                    


                </div>
            
        </div>
    </div>
</article>

    <!-- Footer -->
    <hr>

<!-- Footer -->
<footer>
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <ul class="list-inline text-center">
                    

                    

                    
                        <li>
                            <a href="https://github.com/klugjo/hexo-theme-clean-blog" target="_blank">
                                <span class="fa-stack fa-lg">
                                    <i class="fa fa-circle fa-stack-2x"></i>
                                    <i class="fa fa-github fa-stack-1x fa-inverse"></i>
                                </span>
                            </a>
                        </li>
                    

                    

                    

                    
                </ul>
                <p class="copyright text-muted">&copy; 2020 Cyrus<br></p>
                <p class="copyright text-muted">Original Theme <a target="_blank" href="http://startbootstrap.com/template-overviews/clean-blog/">Clean Blog</a> from <a href="http://startbootstrap.com/" target="_blank">Start Bootstrap</a></p>
                <p class="copyright text-muted">Adapted for <a target="_blank" href="https://hexo.io/">Hexo</a> by <a href="http://www.codeblocq.com/" target="_blank">Jonathan Klughertz</a></p>
                <p class="copyright text-muted"><a href="http://www.beian.miit.gov.cn">粤ICP备18110122号-1</a></p>
            </div>
        </div>
    </div>
</footer>


    <!-- After footer scripts -->
    
<!-- jQuery -->
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>

<!-- Bootstrap -->
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

<!-- Gallery -->
<script src="//cdnjs.cloudflare.com/ajax/libs/featherlight/1.3.5/featherlight.min.js" type="text/javascript" charset="utf-8"></script>

<!-- Disqus Comments -->



</body>

</html>